/***************************************************************************
 *   Copyleft 2008-2009 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./vGlHardwareVertexBuffer.h"
#include "../../Exceptions/vFatalException.h"
#include "GL/glew.h"
#include "../../Macros/vGlMacros.h"

namespace Vendetta {
	vGlHardwareVertexBuffer::vGlHardwareVertexBuffer(vUInt uiSize, vUInt32 ui32Format, 
		vVertexBuffer::eVertexBufferPrimitiveType ePrimitiveType, vVertexBuffer::eVertexBufferUsage eUsage) 
			:vVertexBuffer(uiSize, ui32Format, ePrimitiveType, eUsage), mVerticesID(0), mNormalsID(0),
				mTexCoordsID(0), mColorsID(0) {

		mGlSize = (sizeof(vReal) == sizeof(float)) ? GL_FLOAT : GL_DOUBLE;
		mColorSize = sizeof(vColor) / sizeof(vReal);
		mVertexSize = sizeof(vPoint3D) / sizeof(vReal);
		mTexCoordSize = sizeof(vPoint2D) / sizeof(vReal);

		switch(ePrimitiveType) {
			case VVBPT_QUADS:
				mPrimitiveType = GL_QUADS;
				break;
			case VVBPT_TRIANGLES:
			default:
				mPrimitiveType = GL_TRIANGLES;
				break;
			case VVBPT_TRIANGLE_STRIP:
				mPrimitiveType = GL_TRIANGLE_STRIP;
				break;
		}
	}


	vGlHardwareVertexBuffer::~vGlHardwareVertexBuffer()	{
		if (mFormat & VVBF_UV && mTexCoordsID) {
			glDeleteBuffers(1, &mTexCoordsID); V_GL_ASSERT;
			mTexCoordsID = 0;
		}
		if (mFormat & VVBF_RGBA && mColorsID) {
			glDeleteBuffers(1, &mColorsID); V_GL_ASSERT;
			mColorsID = 0;
		}
		if (mFormat & VVBF_NXNYNZ && mNormalsID) {
			glDeleteBuffers(1, &mNormalsID); V_GL_ASSERT;
			mNormalsID = 0;
		}
		if (mVerticesID) {
			glDeleteBuffers(1, &mVerticesID); V_GL_ASSERT;
			mVerticesID = 0;
		}
	}

	//TODO check if data should be locked before calling use.
	vVoid vGlHardwareVertexBuffer::Use() {
		glEnableClientState(GL_VERTEX_ARRAY);
		if (mFormat & VVBF_UV) {
			glEnableClientState(GL_TEXTURE_COORD_ARRAY); V_GL_ASSERT;
			glBindBuffer(GL_ARRAY_BUFFER, mTexCoordsID); V_GL_ASSERT;
			glTexCoordPointer(mTexCoordSize, mGlSize, 0, vNull); V_GL_ASSERT;
		}
		if (mFormat & VVBF_NXNYNZ) {
			glEnableClientState(GL_NORMAL_ARRAY); V_GL_ASSERT;
			glBindBuffer(GL_ARRAY_BUFFER, mNormalsID); V_GL_ASSERT;
			glNormalPointer(mGlSize, 0, vNull); V_GL_ASSERT;
		}
		if (mFormat & VVBF_RGBA) {
			glEnableClientState(GL_COLOR_ARRAY); V_GL_ASSERT;
			glBindBuffer(GL_ARRAY_BUFFER, mColorsID); V_GL_ASSERT;
			glColorPointer(mColorSize, mGlSize, 0, vNull); V_GL_ASSERT;
		}
		glBindBuffer(GL_ARRAY_BUFFER, mVerticesID); V_GL_ASSERT;
		glVertexPointer(mVertexSize, mGlSize, 0, vNull); V_GL_ASSERT;

		glDrawArrays(mPrimitiveType, 0, mVertexCount);
				
		if (mFormat & VVBF_RGBA) {
			glDisableClientState(GL_COLOR_ARRAY); V_GL_ASSERT;
		}
		if (mFormat & VVBF_NXNYNZ) {
			glDisableClientState(GL_NORMAL_ARRAY); V_GL_ASSERT;
		}
		if (mFormat & VVBF_UV) {
			glDisableClientState(GL_TEXTURE_COORD_ARRAY); V_GL_ASSERT;
		}
		glDisableClientState(GL_VERTEX_ARRAY); V_GL_ASSERT;
	}

	vBool vGlHardwareVertexBuffer::build() {
		vUInt32 glUsage = (mUsage == VVBU_STATIC) ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW;
		
		glGenBuffers(1, &mVerticesID); V_GL_ASSERT;
		glBindBuffer(GL_ARRAY_BUFFER, mVerticesID); V_GL_ASSERT;
		glBufferData(GL_ARRAY_BUFFER, mVertexCount * sizeof(vPoint3D), mpVertices, glUsage); V_GL_ASSERT;
		
		if (mFormat & VVBF_NXNYNZ) {
			glGenBuffers(1, &mNormalsID); V_GL_ASSERT;
			glBindBuffer(GL_ARRAY_BUFFER, mNormalsID); V_GL_ASSERT;
			glBufferData(GL_ARRAY_BUFFER, mNormalCount * sizeof(vPoint3D), mpNormals, glUsage); V_GL_ASSERT;
		}
		if (mFormat & VVBF_UV) {
			glGenBuffers(1, &mTexCoordsID); V_GL_ASSERT;
			glBindBuffer(GL_ARRAY_BUFFER, mTexCoordsID); V_GL_ASSERT;
			glBufferData(GL_ARRAY_BUFFER, mTextCoordCount * sizeof(vPoint2D), mpTexCoords, glUsage); V_GL_ASSERT;
		}
		if (mFormat & VVBF_RGBA) {
			glGenBuffers(1, &mColorsID); V_GL_ASSERT;
			glBindBuffer(GL_ARRAY_BUFFER, mColorsID); V_GL_ASSERT;
			glBufferData(GL_ARRAY_BUFFER, mColorCount * sizeof(vColor), mpColors, glUsage); V_GL_ASSERT;
		}
		return true;
	}

	/************************************************************************/
	/* Disabled                                                             */
	/************************************************************************/
	vGlHardwareVertexBuffer::vGlHardwareVertexBuffer() { 
		V_CHECK_MSG(0, "Some tries to call default vGlHardwareVertexBuffer ctor."); 
	}

	vGlHardwareVertexBuffer::vGlHardwareVertexBuffer(const vGlHardwareVertexBuffer& oGlVertexBuffer) { 
		V_CHECK_MSG(0, "Some tries to call vGlHardwareVertexBuffer copy ctor."); 
	}

	vGlHardwareVertexBuffer& vGlHardwareVertexBuffer::operator=(const vGlHardwareVertexBuffer& oGlVertexBuffer) { 
		V_CHECK_MSG(0, "Some tries to call vGlHardwareVertexBuffer operator=."); 

		return *this; 
	}
	/************************************************************************/
}